//
//     Copyright (c) 2015 Qiwei.Gu(gqwmail@qq.com) All rights reserved.
//
//   Licensed under the Apache License, Version 2.0 (the "License");
//   you may not use this file except in compliance with the License.
//   You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
//   Unless required by applicable law or agreed to in writing, software
//   distributed under the License is distributed on an "AS IS" BASIS,
//   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//   See the License for the sub language governing permissions and
//   limitations under the License.
//
#include "server_cfg.h"

#include <iostream>

#include <boost/property_tree/ptree.hpp>
#include <boost/property_tree/xml_parser.hpp>
namespace bxml = boost::property_tree;

#include <pugixml.hpp>

#include "common/base/sub_server_mgr.h"
#include "common/base/server_mgr.h"
#include "log/log.h"
#include "net/session_mgr.h"
#include "utils/util.h"

ServerNetInfo::ServerNetInfo()
    : serverType(ServerType::eNone)
    , serverID(0)
    , sessionType(SessionType(0))
    , reconnInterval(300)
    , autoInit(true)
{
}

uint64_t ServerNetInfo::getID()
{
    return SessionMgr::makeSessionID(serverType, sessionType, serverID);
}

bool ServerNetInfo::parseXml(const pugi::xml_node& node)
{
    pugi::xml_attribute attr;
    if (!(attr = node.attribute("serverType")))
    {
        std::cerr << __FILE__ << ":" << __LINE__ << "No serverType define" << std::endl;
        return false;
    }
    serverType = static_cast<ServerType>(attr.as_uint());
    serverID = node.attribute("serverID").as_uint();
    if ((attr = node.attribute("sessionType")))
    {
        sessionType = static_cast<SessionType>(attr.as_uint());
    }
    name = node.attribute("name").as_string();
    port = node.attribute("port").as_string();

    if ((attr = node.attribute("addr")))
    {
        addr = attr.as_string();
    }
    if ((attr = node.attribute("retryInterval")))
    {
        reconnInterval = attr.as_uint();
    }
    if ((attr = node.attribute("autoInit")))
    {
        autoInit = attr.as_uint();
    }

    return true;
}

CfgFileInfo::CfgFileInfo() : serverType(ServerType::eNone) {}

ServerCfg::ServerCfg()
{
}

bool ServerCfg::init()
{
    const auto& subMgr = ServerMgr::get().getSubServ();
    if (!subMgr)
    {
        std::cerr << __FILE__ << ":" << __LINE__ << " "
                  << "Sub Server Manager NOT set." << std::endl;
        return false;
    }
    ServerType type = subMgr->getServerType();
    return init(type);
}

bool ServerCfg::init(ServerType type)
{
    pugi::xml_document doc;
    auto  result = doc.load_file("config.xml");
    if (!result)
    {
        std::cerr << __FILE__ << ":" << __LINE__
                  << " Parse config.xml failed. " << result.description() << std::endl;
        return false;
    }

    auto cfgNodes = doc.child("root").child("cfgFiles");
    for (pugi::xml_node& cfg : cfgNodes)
    {
        ServerType serverType = static_cast<ServerType>(cfg.attribute("serverType").as_uint());
        if (serverType != type)
        {
            continue;
        }

        cfgFile_.serverType = serverType;
        cfgFile_.serverName = cfg.attribute("name").as_string();
        cfgFile_.cfgFile    = cfg.child_value();
        break;
    }

    if (cfgFile_.cfgFile.empty())
    {
        std::cerr << __FILE__ << ":" << __LINE__ << " "
                  << "config.xml No find server cfg file define, type: " << uint32_t(type) << std::endl;
        return false;
    }

    doc.reset();
    result = doc.load_file(cfgFile_.cfgFile.c_str());
    if (!result)
    {
        std::cerr << __FILE__ << ":" << __LINE__ << " Parse "
                  << cfgFile_.cfgFile << "  failed. " << result.description() << std::endl;
        return false;
    }

    auto rootNode = doc.child("root");
    logCfgFile_ = rootNode.child("logCfg").child_value();
    if (logCfgFile_.empty())
    {
        std::cerr << __FILE__ << ":" << __LINE__ << " Log config file not set" << std::endl;
        return false;
    }

    auto netNode = rootNode.child("net");

    auto listensNode = netNode.child("listens");
    for (auto& it : listensNode)
    {
        auto info = std::make_shared<ServerNetInfo>();
        if (!info->parseXml(it))
        {
            std::cerr << __FILE__ << ":" << __LINE__ << " Parse net info faile." << std::endl;
            return false;
        }
        if(!netListens_.insert(std::make_pair(info->serverType, info)).second)
        {
            std::cerr << __FILE__ << ":" << __LINE__ << " Server config repeat, type:" << info->serverType
                      << " id:" << info->serverID << std::endl;;
            return false;
        }
    }

    auto connsNodes = netNode.child("connects");
    for (auto& it : connsNodes)
    {
        auto info = std::make_shared<ServerNetInfo>();
        if (!info->parseXml(it))
        {
            std::cerr << __FILE__ << ":" << __LINE__ << " Parse net info faile." << std::endl;
            return false;
        }
        if (info->addr.empty())
        {
            std::cerr << __FILE__ << ":" << __LINE__ << " Connection no addr info,"
                      << " serverType:" << info->serverType
                      << " ServerID:" << info->serverID << std::endl;
            return false;
        }

        netConns_[std::make_pair(info->serverType, info->sessionType)][info->serverID] = info;
    }
    std::cout << __FILE__ << ":" << __LINE__ << " Server config manager init suc." << std::endl;
    return true;
}

/**
 * @desc 创建新的实例
 * @auth Qiwei.Gu
 * @date 2015-04-06 16:22:35
 */
std::shared_ptr<IConfig> ServerCfg::makeNewObject()
{
    return std::make_shared<ServerCfg>();
}

/**
 * @desc 循环遍历监听配置并执行回调函数
 * @auth Qiwei.Gu
 * @date 2015-03-26 22:14:53
 */
bool ServerCfg::loopListens(std::function<bool (const std::shared_ptr<ServerNetInfo>&)> func)
{
    for (auto& it : netListens_)
    {
        if (!func(it.second))
        {
            uint64_t id = SessionMgr::makeSessionID(it.second->serverType,
                                                    it.second->sessionType,
                                                    it.second->serverID);
            LOG_ERROR("create listen failed. ID:" << std::hex << id );
            return false;
        }
    }
    return true;
}

/**
 * @desc 循环遍历连接配置并执行回调函数
 * @auth Qiwei.Gu
 * @date 2015-03-26 22:19:06
 */
bool ServerCfg::loopConns(std::function<bool (const std::shared_ptr<ServerNetInfo>&)> func)
{
    for (auto& it : netConns_)
    {
        for (auto itm : it.second)
        {
            if (!func(itm.second))
            {
                uint64_t id = SessionMgr::makeSessionID(itm.second->serverType,
                                                        itm.second->sessionType,
                                                        itm.second->serverID);
                LOG_ERROR("create conn failed. ID:" << std::hex << id );
                return false;
            }
        }
    }
    return true;
}

/**
 * @desc 查询连接配置
 * @auth Qiwei.Gu
 * @date 2015-04-21 18:33:17
 */
void ServerCfg::getConnNetInfos(ServerType serverType,
                                SessionType sessionType,
                                std::vector<std::shared_ptr<ServerNetInfo>>& netInfos)
{
    const auto& it = netConns_.find(std::make_pair(serverType, sessionType));
    if (it != netConns_.end())
    {
        for (const auto& itn : it->second)
        {
            netInfos.push_back(itn.second);
        }
    }
}
